/**************************************************************************
 * Copyright (C) 2017-2017  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : uni_vui_interface.c
 * Author      : yzs.unisound.com
 * Date        : 2018.06.19
 *
 **************************************************************************/
#include "uni_vui_interface.h"
#include "uni_log.h"
#include "uni_lasr.h"

#define VUI_TAG          "vui"

typedef enum {
  VUI_RECOGN_INIT = 0,
  VUI_RECOGN_STOP,
  VUI_RECOGN_RUNNING,
} VuiRecognStatus;

typedef struct {
  EventHandler    event_handler;
  VuiRecognStatus status;
  uni_u32         session_id;
} Vui;

static Vui g_vui = {NULL, VUI_RECOGN_INIT, 0};

static const char* _status_to_str(VuiRecognStatus status) {
  switch (status) {
    case VUI_RECOGN_RUNNING: return "RECOGN_RUNNING";
    case VUI_RECOGN_STOP:    return "RECOGN_STOP";
    default: break;
  }
  return "N/A";
}

static inline void _set_recogn_status(Vui *vui, VuiRecognStatus status) {
  vui->status = status;
  LOGT(VUI_TAG, "vui set recogn status. %s", _status_to_str(status));
}

static void _create_send_event(char *command, uni_s32 event_type,
                               uni_u32 session_id, void *event_extend_info) {
  Event *event = NULL;
  EventContent event_content;
  uni_memset(&event_content, 0, sizeof(EventContent));
  event_content.info = command;
  event_content.extend_info = event_extend_info;
  event_content.vui_session_id = session_id;
  event = EventCreate(EVENT_SEQUENCE_ID_DEFAULT, event_type, &event_content,
                      DefaultEventContentFreeHandler);
  LOGT(VUI_TAG, "event_type[%d]: %s", event_type, command);
  g_vui.event_handler(event);
  EventFree(event);
  return;
}

static void _got_lasr_result(uni_u32 session_id, const char* content, uni_u32 start, uni_u32 end) {
  uni_time_stamp_t *time_stamp = NULL;
  if (NULL == (time_stamp = uni_malloc(sizeof(uni_time_stamp_t)))) {
    LOGE(VUI_TAG, "alloc memory failed.");
    return ;
  }
  time_stamp->time_len = start;
  time_stamp->end_timestamp = end;
  int result_len = uni_strlen(content);
  char *command = (char *)uni_malloc(result_len + 1);
  if (NULL == command) {
    LOGE(VUI_TAG, "alloc memory failed.");
    uni_free(time_stamp);
    return ;
  }
  uni_strcpy(command, content);
  _create_send_event(command, ID(VUI_LOCAL_ASR_SELF_RESULT_EVENT), session_id,
                    (void *)time_stamp);
}

static void _lasr_timeout(uni_u32 session_id) {
  _create_send_event(NULL, ID(VUI_LOCAL_ASR_TIMEOUT_EVENT), session_id, NULL);
}

VuiHandle VuiHandleCreate(EventHandler vui_event_handler) {
  Vui *vui = &g_vui;
  if (VUI_RECOGN_INIT == g_vui.status) {
    if (0 != uni_lasr_init(_got_lasr_result, _lasr_timeout)) {
      LOGE(VUI_TAG, "uni_lasr_init failed.");
      return NULL;
    }
    vui->event_handler = vui_event_handler;
    _set_recogn_status(vui, VUI_RECOGN_STOP);
  }
  return (VuiHandle)vui;
}

Result VuiHandleDestroy(VuiHandle handle) {
  Vui *vui = (Vui *)handle;
  if (NULL == vui) {
    LOGE(VUI_TAG, "handle=%p", handle);
    return E_FAILED;
  }
  uni_lasr_final();
  return E_OK;
}

Result VuiRecognStart(VuiHandle handle, VuiRecognModel mode_id, uni_u32 timeout) {
  Vui *vui = (Vui *)handle;
  Result ret = E_OK;
  if (NULL == vui) {
    LOGE(VUI_TAG, "recogn start failed. handle=%p", handle);
    return E_FAILED;
  }
  if (VUI_RECOGN_RUNNING == vui->status) {
    LOGW(VUI_TAG, "recogn status[%s], cannot start again.",
         _status_to_str(VUI_RECOGN_RUNNING));
    return E_FAILED;
  }
  vui->session_id ++;
  switch (mode_id) {
    case UNI_LP_WAKEUP:
      ret = uni_lasr_start(vui->session_id, LASR_MODE_WAKEUP, timeout);
      break;
    case UNI_LP_LASR:
      ret = uni_lasr_start(vui->session_id, LASR_MODE_CMD, timeout);
      break;
    case UNI_RASR_LASR_MODE:
      ret = uni_lasr_start(vui->session_id, LASR_MODE_CMD, timeout);
      break;
    default:
      LOGW(VUI_TAG, "unknow VUI mode: %d", mode_id);
      return E_FAILED;
  }
  if (E_OK != ret) {
    LOGD(VUI_TAG, "recogn start faild. mode_id=%d", mode_id);
    return E_FAILED;
  }
  _set_recogn_status(vui, VUI_RECOGN_RUNNING);
  LOGD(VUI_TAG, "recogn start success. mode_id=%d", mode_id);
  return E_OK;
}

Result VuiRecognStop(VuiHandle handle) {
  Vui *vui = (Vui *)handle;
  if (NULL == vui) {
    LOGE(VUI_TAG, "handle=%p.", handle);
    return E_FAILED;
  }
  if (VUI_RECOGN_STOP == vui->status) {
    LOGW(VUI_TAG, "recogn status[%s], cannot stop again.",
         _status_to_str(VUI_RECOGN_STOP));
    return E_FAILED;
  }
  uni_lasr_stop();
  _set_recogn_status(vui, VUI_RECOGN_STOP);
  LOGD(VUI_TAG, "recogn stop success.");
  return E_OK;
}

Result VuiRecognCancel(VuiHandle handle) {
  Vui *vui = (Vui *)handle;
  if (NULL == vui) {
    LOGE(VUI_TAG, "handle=%p", handle);
    return E_FAILED;
  }
  if (VUI_RECOGN_STOP == vui->status) {
    LOGW(VUI_TAG, "recogn status[%s], cannot cancel again.",
         _status_to_str(VUI_RECOGN_STOP));
    return E_FAILED;
  }
  uni_lasr_stop();
  _set_recogn_status(vui, VUI_RECOGN_STOP);
  LOGD(VUI_TAG, "recogn cancel success.");
  return E_OK;
}

uni_u32 VuiRecognGetSessionId(VuiHandle handle) {
  Vui *vui = (Vui *)handle;
  if (vui) {
    return vui->session_id;
  } else {
    return (uni_u32)-1;
  }
}

